package com.hanock.sm.fork2d;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.microedition.khronos.opengles.GL10;


import android.graphics.Rect;
import android.graphics.RectF;

public class Node implements Comparator<Node>{
	
	private Node parent;
	protected List<Node> childList;
	private List<Transform> transformList;
	
	private int tag;
	private Object data;
	private BlendMode blendMode;

	
	private float x,y;
	private float width, height;
	private float ax,ay; // anchor, default 0.5, 0.5 / 앵커 = 액자의 못
	
	private float r,g,b,a; // color [0,1]

	//왠만하면 쓰지말것
	private float tx,ty; // traslate 평행이동 
	private float sx,sy; // scale. x,y를 기준으로 확대축소 / 대신 부모의 스케일도 모두 영향받음(각각 자신의 x,y)
	private float rotate; //자기자신에서는 앵커기준
	
	
	private int zOrder;
	
	private boolean enabled;
	private boolean touchable;
	private boolean progressable;
	
	
	public Node() {
		childList = new ArrayList<Node>();
		r = g = b = a = 1;
		ax = ay = 0.5f;
		sx = sy = 1;
		blendMode = BlendMode.NORMAL;
		
		enabled = true;
		touchable = true;
		progressable = true;
	}
	
	// ----------------------------------------------------------------------
	// Life Cycle Functions
	// ----------------------------------------------------------------------

	// ----------------------------------------------------------------------
	// Tree Functions
	// ----------------------------------------------------------------------
	
	public void visit(Canvas canvas){
		
		if (!enabled)
			return;
		
		GL10 gl = canvas.getGL();
		
		boolean transform = isTransform();
		if (transform){
			gl.glMatrixMode(GL10.GL_MODELVIEW);
			gl.glPushMatrix();//트랜스폼의 계산 순서 스택
			
			gl.glTranslatef(tx, ty, 0);
			gl.glTranslatef(x, y, 0);
			gl.glRotatef(rotate, 0, 0, 1);
			gl.glScalef(sx, sy, 1);
			gl.glTranslatef(-x, -y, 0);
		}
		
		for (int i = 0; i < childList.size(); i++){
			if (childList.get(i).getZOrder() < zOrder)
				childList.get(i).visit(canvas);
		}
		
		switch (blendMode) {
		case NORMAL:
			gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
			break;
		case SCREEN:
			gl.glBlendFunc(GL10.GL_ONE_MINUS_DST_COLOR, GL10.GL_ONE);
			break;
		case ADD:
			gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
			break;
		case MULTIPLY:
			gl.glBlendFunc(GL10.GL_DST_COLOR, GL10.GL_ZERO);
			break;
		default:
			break;
		}
		
		gl.glColor4f(r, g, b, a);
		draw(canvas);
		gl.glColor4f(1, 1, 1, 1);
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		
		for (int i = 0; i < childList.size(); i++){
			if (childList.get(i).getZOrder() >= zOrder)
				childList.get(i).visit(canvas);
		}
		
		if (transform){
			gl.glPopMatrix();
		}
	}

	public boolean visit(float x, float y, TouchMode mode){
		
		if (!enabled || !touchable)
			return false;
		
		boolean b = false;
		
		for (int i = childList.size() - 1; i >= 0; i--){
			if (childList.get(i).getZOrder() > zOrder){
				b = childList.get(i).visit(x, y, mode);
				if (b)
					return true;
			}
		}
		
		b = onTouch(x, y, mode);
		if (b)
			return true;
		
		for (int i = childList.size() - 1; i >= 0; i--){
			if (childList.get(i).getZOrder() <= zOrder){
				b = childList.get(i).visit(x, y, mode);
				if (b)
					return true;
			}
		}
		
		return false;
	}
	
	public void visit(int elapsed){
		
		if (!progressable || !enabled)
			return;
		
		progress(elapsed);
		
		for (int i = 0; i < childList.size(); i++){
			childList.get(i).visit(elapsed);
		}
	}
	
	public void removeChildren(){
		childList.clear();
	}
	
	public void remove(Node node){
		childList.remove(node);
	}

	public void remove(){
		if (parent != null){
			parent.remove(this);
			parent = null;
		}
	}
	
	public void onChildZOrderChange(Node child){
		if (!childList.contains(child))
			return;
		
		childList.remove(child);
		add(child);
	}
	
	public void add(Node node){
		node.setParent(this);
		
		if (childList.size() == 0){
			childList.add(node);
			return;
		}
		
		for (int i = 0; i < childList.size(); i++){
			if (childList.get(i).getZOrder() > node.getZOrder()){
				childList.add(i, node);
				return;
			}
		}
		
		childList.add(node);
	}
	
	// ----------------------------------------------------------------------
	// Touch Functions
	// ----------------------------------------------------------------------
	
	protected boolean onTouch(float x, float y, TouchMode mode){
		
		return false;
	}
	
	// ----------------------------------------------------------------------
	// Graphical Functions
	// ----------------------------------------------------------------------
	
	protected void draw(Canvas canvas){
		
	}

	public void getDesRect(RectF desRect){
		desRect.left = x - ax*width;
		desRect.right = desRect.left + width;
		
		desRect.bottom = y - ay*height;
		desRect.top = desRect.bottom + height;
	}
	
	public void addTransform(Transform transform){
		if (transformList == null)
			transformList = new ArrayList<Transform>(2);
		transformList.add(transform);
	}
	
	public void clearTransform(){
		if (transformList != null)
			transformList.clear();
	}
	
	public void rotate(float ang){
		rotate += ang;
	}
	
	public boolean isTransform(){
		return ! (sx == 1 && sy == 1 && tx == 0 && ty == 0 && rotate == 0);
	}
	
	// ----------------------------------------------------------------------
	// Progress Functions
	// ----------------------------------------------------------------------
	
	public void progress(int elapsed){
		
		if (transformList == null)
			return;
		
		for (int i = 0; i < transformList.size(); i++){
			Transform transform = transformList.get(i);
			transform.progress(this, elapsed);
			
			if (transform.isFinished()){
				transform.onFinish(this);
				transformList.remove(i);
				i--;
			}
		}
	}
	
	// ----------------------------------------------------------------------
	// Convenient Setters Functions
	// ----------------------------------------------------------------------
	
	public void setColorf(float r, float g, float b, float a){
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = a;
	}
	
	public void setColori(int r, int g, int b, int a){
		this.r = r/255f;
		this.g = g/255f;
		this.b = b/255f;
		this.a = a/255f;
	}
	
	public void setRotate(float rotate){
		this.rotate = rotate;
	}

	public void setPosition(float x, float y){
		this.x = x;
		this.y = y;
	}
	
	public void setTranslate(float tx, float ty){
		this.tx = tx;
		this.ty = ty;
	}
	
	public void setScale(float sx, float sy){
		this.sx = sx;
		this.sy = sy;
	}
	
	public void setAnchor(float ax, float ay){
		this.ax = ax;
		this.ay = ay;
	}
	
	public void setSize(float width, float height){
		this.width = width;
		this.height = height;
	}
	
	public void set(RectF rect){
		set(rect.left, rect.top, rect.right, rect.bottom);
	}
	
	public void set(Rect rect){
		set(rect.left, rect.top, rect.right, rect.bottom);
	}
	
	public void set(int left, int top, int right, int bottom) {
		setSize(Math.abs(right - left), Math.abs(top - bottom));
		setPosition(left + ax*getWidth(), bottom + ay*getHeight()); 
	}
	
	public void set(float left, float top, float right, float bottom) {
		setSize(Math.abs(right - left), Math.abs(top - bottom));
		setPosition(left + ax*getWidth(), bottom + ay*getHeight()); 
	}
	
	public void addTranslate(float dx, float dy){
		tx += dx;
		ty += dy;
	}
	
	public void addRotate(float angle){
		rotate += angle;
	}
	
	// ----------------------------------------------------------------------
	// Getters & Setters Functions
	// ----------------------------------------------------------------------
	
	public void setBlendMode(BlendMode blendMode) {
		if (blendMode == null)
			return;
		this.blendMode = blendMode;
	}
	
	public BlendMode getBlendMode() {
		return blendMode;
	}
	
	public float getRotate() {
		return rotate;
	}
	
	public boolean isTouchable() {
		return touchable;
	}
	
	public void setTouchable(boolean touchable) {
		this.touchable = touchable;
	}
	
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	
	public Node getParent() {
		return parent;
	}

	public void setParent(Node parent) {
		this.parent = parent;
	}
	
	public int getTag() {
		return tag;
	}

	public void setTag(int tag) {
		this.tag = tag;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}
	
	public int getZOrder() {
		return zOrder;
	}

	public void setZOrder(int zOrder) {
		this.zOrder = zOrder;
		if (parent != null){
			parent.onChildZOrderChange(this);
		}
	}

	public float getX() {
		return x;
	}

	public void setX(float x) {
		this.x = x;
	}
	
	public float getTranslateX() {
		return tx;
	}
	
	public void setTranslateX(float tx) {
		this.tx = tx;
	}
	
	public float getTranslateY() {
		return ty;
	}
	
	public void setTranslateY(float ty) {
		this.ty = ty;
	}

	public float getY() {
		return y;
	}

	public void setY(float y) {
		this.y = y;
	}
	
	public float getAlpha() {
		return a;
	}
	
	public void setAlpha(float alpha) {
		this.a = alpha;
	}

	public float getWidth() {
		return width;
	}

	public void setWidth(float width) {
		this.width = width;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	public float getAnchorX() {
		return ax;
	}

	public void setAnchorX(float ax) {
		this.ax = ax;
	}

	public float getAnchorY() {
		return ay;
	}

	public void setAnchorY(float ay) {
		this.ay = ay;
	}

	public float getScaleX() {
		return sx;
	}
	
	public void setScaleX(float sx) {
		this.sx = sx;
	}
	
	public float getScaleY() {
		return sy;
	}
	
	public void setScaleY(float sy) {
		this.sy = sy;
	}
	
	public float getRed(){
		return r;
	}
	
	public void setRed(float r){
		this.r = r;
	}
	
	public float getBlue(){
		return b;
	}
	
	public void setBlue(float b){
		this.b = b;
	}
	
	public float getGreen(){
		return g;
	}
	
	public void setGreen(float g){
		this.g = g;
	}
	
	public List<Node> getChildList() {
		return childList;
	}
	
	public boolean isEnabled() {
		return enabled;
	}
	
	public boolean isProgressable() {
		return progressable;
	}
	
	public void setProgressable(boolean progressable) {
		this.progressable = progressable;
	}
	
	// ----------------------------------------------------------------------
	// Comparator
	// ----------------------------------------------------------------------
	
	@Override
	public int compare(Node n1, Node n2) {
		return n1.getZOrder() - n2.getZOrder();
	}
}

































